{
  "name": "Case 71 - Product Manager Feature Praise & Enhancement Requests",
  "nodes": [
    {
      "parameters": {
        "rule": {
          "interval": [
            {
              "field": "weeks",
              "triggerAtDay": [
                1
              ],
              "triggerAtHour": 9
            }
          ]
        }
      },
      "type": "n8n-nodes-base.scheduleTrigger",
      "typeVersion": 1.2,
      "position": [
        -896,
        -144
      ],
      "id": "01b79d54-4b2b-4a04-b978-cfe8e7b950f5",
      "name": "Weekly Schedule - Monday 9 AM"
    },
    {
      "parameters": {
        "actorId": {
          "__rl": true,
          "value": "Xb8osYTtOjlsgI6k9",
          "mode": "list",
          "cachedResultName": "Google Maps Reviews Scraper (compass/Google-Maps-Reviews-Scraper)",
          "cachedResultUrl": "https://console.apify.com/actors/Xb8osYTtOjlsgI6k9/input"
        },
        "customBody": "{\n  \"language\": \"en\",\n  \"maxReviews\": 100,\n  \"personalData\": true,\n  \"startUrls\": [\n    {\n      \"url\": \"https://www.google.com/maps/place/Trader+Joe's/@40.733375,-73.9904715,17z/data=!4m16!1m9!3m8!1s0x89c2599f28229a6b:0xd6b212541efb45e0!2sTrader+Joe's!8m2!3d40.733371!4d-73.9878966!9m1!1b1!16s%2Fg%2F1txc7yym!3m5!1s0x89c2599f28229a6b:0xd6b212541efb45e0!8m2!3d40.733371!4d-73.9878966!16s%2Fg%2F1txc7yym?entry=ttu&g_ep=EgoyMDI1MTIwOS4wIKXMDSoASAFQAw%3D%3D\"\n    }\n  ],\n  \"reviewsSort\": \"newest\",\n  \"reviewsOrigin\": \"all\"\n}"
      },
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 1,
      "position": [
        -704,
        -144
      ],
      "id": "4ddcd206-9a12-41be-b033-0858ba688d04",
      "name": "Scrape Store Reviews",
      "credentials": {
        "apifyApi": {
          "id": "w5S6YBbbyUddEfQA",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "resource": "Datasets",
        "datasetId": "={{ $json.defaultDatasetId }}",
        "limit": 100
      },
      "type": "@apify/n8n-nodes-apify.apify",
      "typeVersion": 1,
      "position": [
        -512,
        -144
      ],
      "id": "c95e1448-905b-45c8-8ad1-f6b678f21f71",
      "name": "Get Dataset Items",
      "credentials": {
        "apifyApi": {
          "id": "w5S6YBbbyUddEfQA",
          "name": "Apify account"
        }
      }
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 1
          },
          "conditions": [
            {
              "id": "filter-4star",
              "leftValue": "={{ $json.stars }}",
              "rightValue": 4,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            },
            {
              "id": "filter-5star",
              "leftValue": "={{ $json.stars }}",
              "rightValue": 5,
              "operator": {
                "type": "number",
                "operation": "equals"
              }
            }
          ],
          "combinator": "or"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.filter",
      "typeVersion": 2,
      "position": [
        -320,
        -144
      ],
      "id": "2b70ef61-6555-49db-8618-5197b0433bee",
      "name": "Filter 4-5 Star Reviews"
    },
    {
      "parameters": {
        "jsCode": "// Filter out reviews without meaningful text\nconst items = $input.all();\nconst validItems = [];\n\nitems.forEach((item, index) => {\n  const text = item.json.text || item.json.textTranslated || '';\n  \n  // Skip if text is empty or too short\n  if (text.trim().length < 10) {\n    console.log(`Skipping item ${index}: Empty or too short review text`);\n    return;\n  }\n  \n  validItems.push(item);\n});\n\nconsole.log(`Filtered: ${items.length} total → ${validItems.length} valid reviews with text`);\n\nreturn validItems;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        -144,
        -144
      ],
      "id": "46195605-1e1d-4209-927a-0690305a7abb",
      "name": "Validate Review Text"
    },
    {
      "parameters": {
        "modelId": {
          "__rl": true,
          "value": "gpt-4o",
          "mode": "list",
          "cachedResultName": "GPT-4o"
        },
        "responses": {
          "values": [
            {
              "role": "system",
              "content": "You are a Product Manager analyzing customer feedback. Read the positive review and identify feature praise AND enhancement requests.\n\nReturn ONLY this exact JSON format with NO other text:\n\n{\n  \"insight_category\": \"Feature Appreciation|Enhancement Requests|Use Case Discoveries|Accessory Needs|Service Innovations|Customer Segment Insights|Other\",\n  \"insight_value\": \"high|medium|low\",\n  \"actionability\": \"Product Roadmap|In-Store Experience|Marketing Content|Staff Training|Accessory Development|No Action Needed\",\n  \"key_insight\": \"specific feature loved or improvement suggested\",\n  \"key_quote\": \"most important sentence from review\"\n}\n\nCATEGORIES:\n\nFeature Appreciation = Specific product features customers love\nKeywords: love the, amazing feature, best part is, incredible that it\nExample: \"Love the Today at Apple sessions\" → Feature Appreciation\n\nEnhancement Requests = Suggestions for improvement (even in positive reviews)\nKeywords: wish there were, would be better if, only thing missing, hope they add\nExample: \"Wish there were more sessions available\" → Enhancement Requests\n\nUse Case Discoveries = How customers actually use products (often surprising)\nKeywords: I use it for, perfect for, helps me with, great for\nExample: \"Use AirDrop to share photos with grandkids\" → Use Case Discoveries\n\nAccessory Needs = Requests for accessories, add-ons, compatibility\nKeywords: need a case, cable, charger, works with, compatible\nExample: \"Lightning cable demo was helpful\" → Accessory Needs\n\nService Innovations = In-store experience, staff support, workshops\nKeywords: staff helped, genius bar, workshop, demonstration, tutorial\nExample: \"Staff demo helped me understand\" → Service Innovations\n\nCustomer Segment Insights = Demographics, user types revealed\nKeywords: as a student, for my business, seniors, families, professionals\nExample: \"Perfect for my photography business\" → Customer Segment Insights\n\nOther = Everything else\n\nINSIGHT VALUE:\n- high: Frequently mentioned, strong emotion, actionable insight\n- medium: Notable mention, moderate importance\n- low: Nice to have, minor observation\n\nACTIONABILITY:\n- Product Roadmap: Feature requests, product improvements\n- In-Store Experience: Store layout, displays, demos\n- Marketing Content: Testimonial-worthy, use case for campaigns\n- Staff Training: Service excellence examples to replicate\n- Accessory Development: Accessory/add-on opportunities\n- No Action Needed: General praise, no specific action\n\nReturn ONLY the JSON. No explanation. Start with { and end with }"
            },
            {
              "content": "=Analyze this customer review:\n\nStore: {{ $json.title }}\nCustomer: {{ $json.name }}\nRating: {{ $json.stars }} stars\nDate: {{ $json.publishedAtDate }}\nReview: {{ $json.text }}\n\nReturn only JSON."
            }
          ]
        },
        "builtInTools": {},
        "options": {
          "maxTokens": 500,
          "temperature": 0
        }
      },
      "type": "@n8n/n8n-nodes-langchain.openAi",
      "typeVersion": 2.1,
      "position": [
        32,
        -144
      ],
      "id": "6498961b-cbb8-43b0-a74a-2cef302d4adc",
      "name": "AI: Analyze Product Feedback",
      "credentials": {
        "openAiApi": {
          "id": "ICwxUBbatsF2sDvy",
          "name": "OpenAi account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "// ENHANCED PARSER WITH VALIDATION\nconst items = [];\nconst input = $input.all();\n\nfunction extractJSON(text) {\n  const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n  if (!jsonMatch) return null;\n  return jsonMatch[0];\n}\n\ninput.forEach((item, index) => {\n  try {\n    let aiText = item.json.output[0].content[0].text || '';\n    \n    // Clean markdown and extra text\n    aiText = aiText\n      .replace(/```json/gi, '')\n      .replace(/```/g, '')\n      .trim();\n    \n    // Extract JSON\n    const jsonStr = extractJSON(aiText);\n    \n    if (!jsonStr) {\n      throw new Error('No JSON found in response');\n    }\n    \n    // Parse\n    const parsed = JSON.parse(jsonStr);\n    \n    // Validate required fields\n    if (!parsed.insight_category || !parsed.insight_value || !parsed.actionability) {\n      throw new Error('Missing required fields');\n    }\n    \n    items.push({ json: parsed });\n    \n  } catch (error) {\n    console.error(`Parse error for item ${index}:`, error.message);\n    \n    items.push({\n      json: {\n        insight_category: 'Other',\n        insight_value: 'low',\n        actionability: 'No Action Needed',\n        key_insight: 'Parse error',\n        key_quote: ''\n      }\n    });\n  }\n});\n\nreturn items;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        336,
        -144
      ],
      "id": "aafd3808-9e2b-4cd0-91d1-7c7de91688d0",
      "name": "Parse AI Response"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "cat",
              "name": "insight_category",
              "value": "={{ $json.insight_category }}",
              "type": "string"
            },
            {
              "id": "value",
              "name": "insight_value",
              "value": "={{ $json.insight_value }}",
              "type": "string"
            },
            {
              "id": "action",
              "name": "actionability",
              "value": "={{ $json.actionability }}",
              "type": "string"
            },
            {
              "id": "insight",
              "name": "key_insight",
              "value": "={{ $json.key_insight && $json.key_insight.includes('Parse error') ? 'Not sufficient data available' : $json.key_insight }}",
              "type": "string"
            },
            {
              "id": "quote",
              "name": "key_quote",
              "value": "={{ $json.key_quote && $json.key_quote !== '' ? $json.key_quote : 'No quote available' }}",
              "type": "string"
            },
            {
              "id": "date",
              "name": "review_date",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.publishedAtDate }}",
              "type": "string"
            },
            {
              "id": "customer",
              "name": "customer_name",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.name }}",
              "type": "string"
            },
            {
              "id": "rating",
              "name": "star_rating",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.stars }}",
              "type": "number"
            },
            {
              "id": "text",
              "name": "review_text",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.textTranslated || $('Filter 4-5 Star Reviews').item.json.text }}",
              "type": "string"
            },
            {
              "id": "url",
              "name": "review_url",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.reviewUrl }}",
              "type": "string"
            },
            {
              "id": "store",
              "name": "store_location",
              "value": "={{ $('Filter 4-5 Star Reviews').item.json.address }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        560,
        -144
      ],
      "id": "9218f7bb-f4cd-4f3c-b2ee-5920cd6cbbef",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "operation": "append",
        "documentId": {
          "__rl": true,
          "value": "1loHHGyMlfuiCGPtLa-iwgI-IT8g2vhjnz07H-wmGF9U",
          "mode": "list",
          "cachedResultName": "Case 71 - Product manager sheet headers",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1loHHGyMlfuiCGPtLa-iwgI-IT8g2vhjnz07H-wmGF9U/edit?usp=drivesdk"
        },
        "sheetName": {
          "__rl": true,
          "value": "gid=0",
          "mode": "list",
          "cachedResultName": "Sheet1",
          "cachedResultUrl": "https://docs.google.com/spreadsheets/d/1loHHGyMlfuiCGPtLa-iwgI-IT8g2vhjnz07H-wmGF9U/edit#gid=0"
        },
        "columns": {
          "mappingMode": "defineBelow",
          "value": {
            "Review_Date": "={{ $json.review_date }}",
            "Customer_Name": "={{ $json.customer_name }}",
            "Star_Rating": "={{ $json.star_rating }}",
            "Review_Text": "={{ $json.review_text }}",
            "Insight_Category": "={{ $json.insight_category }}",
            "Insight_Value": "={{ $json.insight_value }}",
            "Actionability": "={{ $json.actionability }}",
            "Key_Insight": "={{ $json.key_insight }}",
            "Key_Quote": "={{ $json.key_quote }}",
            "Review_URL": "={{ $json.review_url }}"
          },
          "matchingColumns": [],
          "schema": [
            {
              "id": "Review_Date",
              "displayName": "Review_Date",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Customer_Name",
              "displayName": "Customer_Name",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Star_Rating",
              "displayName": "Star_Rating",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Review_Text",
              "displayName": "Review_Text",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Insight_Category",
              "displayName": "Insight_Category",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Insight_Value",
              "displayName": "Insight_Value",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Actionability",
              "displayName": "Actionability",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Key_Insight",
              "displayName": "Key_Insight",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Key_Quote",
              "displayName": "Key_Quote",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            },
            {
              "id": "Review_URL",
              "displayName": "Review_URL",
              "required": false,
              "defaultMatch": false,
              "display": true,
              "type": "string",
              "canBeUsedToMatch": true
            }
          ],
          "attemptToConvertTypes": false,
          "convertFieldsToString": false
        },
        "options": {}
      },
      "type": "n8n-nodes-base.googleSheets",
      "typeVersion": 4.5,
      "position": [
        752,
        -144
      ],
      "id": "0da866a0-7a4a-4f29-b936-28b118fd0839",
      "name": "Log to Google Sheet",
      "credentials": {
        "googleSheetsOAuth2Api": {
          "id": "LOs2dbk9lby0NfDM",
          "name": "Google Sheets account"
        }
      }
    },
    {
      "parameters": {
        "jsCode": "const items = $('Edit Fields').all();\n\nif (items.length === 0) {\n  return [{ json: { total_positive_reviews: 0, category_breakdown: {}, value_breakdown: {}, actionability_breakdown: {}, top_insights: [], store_location: 'Unknown' } }];\n}\n\nconst categoryCount = {};\nconst valueCount = {};\nconst actionabilityCount = {};\nconst allInsights = [];\nconst store_location = items[0].json.store_location || 'Unknown';\n\nitems.forEach(item => {\n  const data = item.json;\n  const category = data.insight_category || 'Other';\n  categoryCount[category] = (categoryCount[category] || 0) + 1;\n  const value = data.insight_value || 'low';\n  valueCount[value] = (valueCount[value] || 0) + 1;\n  const action = data.actionability || 'No Action Needed';\n  actionabilityCount[action] = (actionabilityCount[action] || 0) + 1;\n  \n  allInsights.push({\n    insight: data.key_insight || '',\n    quote: data.key_quote || '',\n    customer: data.customer_name || 'Anonymous',\n    date: data.review_date || '',\n    rating: data.star_rating || 0,\n    url: data.review_url || '',\n    insight_value: data.insight_value || 'low',\n    category: data.insight_category || 'Other',\n    actionability: data.actionability || 'No Action Needed'\n  });\n});\n\nconst valueOrder = { high: 3, medium: 2, low: 1 };\nallInsights.sort((a, b) => (valueOrder[b.insight_value] || 0) - (valueOrder[a.insight_value] || 0));\nconst topInsights = allInsights.slice(0, 10);\n\nreturn [{ json: { total_positive_reviews: items.length, category_breakdown: categoryCount, value_breakdown: valueCount, actionability_breakdown: actionabilityCount, top_insights: topInsights, store_location: store_location } }];"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        944,
        -144
      ],
      "id": "476b21e4-5783-4d0c-aa58-fda5817ab84e",
      "name": "Aggregate Product Insights"
    },
    {
      "parameters": {
        "sendTo": "productmanager@company.com",
        "subject": "=Weekly Product Feedback Insights - {{ $json.store_location }}",
        "emailType": "text",
        "message": "=WEEKLY PRODUCT FEEDBACK INSIGHTS\n====================================\n{{ $json.store_location }}\n\nOVERVIEW:\nTotal Positive Reviews Analyzed: {{ $json.total_positive_reviews }}\n\nINSIGHT CATEGORIES:\n{{ Object.keys($json.category_breakdown).map(cat => cat + ': ' + $json.category_breakdown[cat]).join('\\n') }}\n\nINSIGHT VALUE DISTRIBUTION:\n{{ Object.keys($json.value_breakdown).map(val => val.toUpperCase() + ': ' + $json.value_breakdown[val]).join('\\n') }}\n\nACTIONABILITY BREAKDOWN:\n{{ Object.keys($json.actionability_breakdown).map(action => action + ': ' + $json.actionability_breakdown[action]).join('\\n') }}\n\nTOP 10 CUSTOMER INSIGHTS:\n{{ $json.top_insights.map((insight, i) => (i+1) + '. [' + insight.insight_value.toUpperCase() + ' VALUE] ' + insight.category + '\\n   Action Type: ' + insight.actionability + '\\n   Insight: ' + insight.insight + '\\n   Quote: \"' + insight.quote + '\"\\n   - ' + insight.customer + ' | ⭐' + insight.rating + '/5 | ' + insight.date).join('\\n\\n') }}\n\nPRODUCT ROADMAP RECOMMENDATIONS:\n{{ $json.actionability_breakdown['Product Roadmap'] ? '- ' + $json.actionability_breakdown['Product Roadmap'] + ' feature requests identified for engineering review' : '- No feature requests this week' }}\n{{ $json.actionability_breakdown['Accessory Development'] ? '- ' + $json.actionability_breakdown['Accessory Development'] + ' accessory opportunities identified' : '' }}\n\nMARKETING OPPORTUNITIES:\n{{ $json.actionability_breakdown['Marketing Content'] ? '- ' + $json.actionability_breakdown['Marketing Content'] + ' testimonial-worthy reviews for campaigns' : '- No new marketing content this week' }}\n\nIN-STORE EXPERIENCE:\n{{ $json.actionability_breakdown['In-Store Experience'] ? '- ' + $json.actionability_breakdown['In-Store Experience'] + ' in-store improvements suggested' : '- No in-store feedback this week' }}\n{{ $json.actionability_breakdown['Staff Training'] ? '- ' + $json.actionability_breakdown['Staff Training'] + ' service excellence examples for staff training' : '' }}\n\nNEXT STEPS:\n1. Review high-value insights for Q2 roadmap planning\n2. Share enhancement requests with engineering team\n3. Extract quotes for marketing campaigns\n4. Analyze use case discoveries for positioning strategy\n\nGenerated: {{ new Date().toLocaleDateString() }}",
        "options": {}
      },
      "type": "n8n-nodes-base.gmail",
      "typeVersion": 2.1,
      "position": [
        1136,
        -144
      ],
      "id": "4f14c1de-c63f-483f-8113-2873a93654a9",
      "name": "Send Weekly Insights Email",
      "webhookId": "92f59a36-2eee-4af1-bf36-5a3b7cc09e04",
      "credentials": {
        "gmailOAuth2": {
          "id": "cyqCGWcggZNMcSOv",
          "name": "Gmail account"
        }
      }
    }
  ],
  "pinData": {},
  "connections": {
    "Weekly Schedule - Monday 9 AM": {
      "main": [
        [
          {
            "node": "Scrape Store Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Scrape Store Reviews": {
      "main": [
        [
          {
            "node": "Get Dataset Items",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get Dataset Items": {
      "main": [
        [
          {
            "node": "Filter 4-5 Star Reviews",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Filter 4-5 Star Reviews": {
      "main": [
        [
          {
            "node": "Validate Review Text",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Validate Review Text": {
      "main": [
        [
          {
            "node": "AI: Analyze Product Feedback",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI: Analyze Product Feedback": {
      "main": [
        [
          {
            "node": "Parse AI Response",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Parse AI Response": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Log to Google Sheet",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Log to Google Sheet": {
      "main": [
        [
          {
            "node": "Aggregate Product Insights",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Aggregate Product Insights": {
      "main": [
        [
          {
            "node": "Send Weekly Insights Email",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "90f9c55b-2c0d-4d07-bea2-740a6df2b0e6",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "3a43da28588548e21903e71cf1dc3ddd65c24bf0c62e7e4b77542ffe87ad79c6"
  },
  "id": "M9l0xJLSzLOyFiO2",
  "tags": []
}